package com.bluedot.www.framework.mvc.servlet.http;

import com.bluedot.www.core.common.HandwritingRuntimeException;
import com.bluedot.www.core.common.ResultCodeEnum;
import com.bluedot.www.framework.mvc.annotation.MyRequestBody;
import com.bluedot.www.framework.mvc.annotation.MyRequestParam;
import com.google.gson.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Http参数转换
 *
 * @author He Peng
 * @version 1.0
 * @date 2021/8/5 12:39
 */
public class HttpMessageConverter {

    /**
     * <p>正则表达式，判断是否是数字</p>包括String,Integer,Long,Float类型
     */
    private final static Pattern NUMBER_PATTERN = Pattern.compile("^\\-?[0-9]+(.[0-9]+)?$");
    /**
     * 直接从url获取参数的数据类型
     */
    private final static List<String> EXCLUDE = new ArrayList<>();

    static {
        EXCLUDE.add(String.class.getSimpleName());
        EXCLUDE.add(Integer.class.getSimpleName());
        EXCLUDE.add(Long.class.getSimpleName());
        EXCLUDE.add(Float.class.getSimpleName());
    }

    /**
     * 日志，用于记录特定系统或应用的消息
     */
    private final Logger log = Logger.getLogger(HttpMessageConverter.class.getName());

    public HttpMessageConverter() {

    }

    /**
     * 将json转为Java对象
     *
     * @param json:  json字符串
     * @param clazz: class对象
     * @return java.lang.Object
     * @author Jiutwo
     * @date 2021/8/5 11:58
     */
    public static <T> Object fromJson(String json, Class<T> clazz) {
        Gson gson = new GsonBuilder()
                .setLenient()
                .enableComplexMapKeySerialization()
                .serializeNulls()   // 智能null
                .setPrettyPrinting()
                .disableHtmlEscaping()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .create();
        return gson.fromJson(json, clazz);
    }


    /**
     * 将Java对象转为Json格式
     *
     * @param obj: 后端返回的对象
     * @return java.lang.String
     * @author Jiutwo
     * @date 2021/8/5 10:40
     */
    public static String toJson(Object obj) {
        Gson gson = new GsonBuilder()
                .setLenient()
                .enableComplexMapKeySerialization()
                .serializeNulls()   // 智能null
                .setPrettyPrinting()
                .disableHtmlEscaping()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .create();
        return gson.toJson(obj);
    }

    /**
     * 判断字符串是否符合json规范
     *
     * @param jsonStr: json字符串
     * @return boolean
     * @author He Peng
     * @date 2021/8/24 20:45
     */
    public static boolean validateJson(String jsonStr) {
        JsonElement jsonElement;
        try {
            jsonElement = JsonParser.parseString(jsonStr);
        } catch (JsonSyntaxException e) {
            // 不打印异常信息
            return false;
        }
        if (jsonElement == null) {
            return false;
        }
        return jsonElement.isJsonArray() || jsonElement.isJsonObject();

/*//        先转JsonObject
        JsonObject jsonObject = JsonParser.parseString(jsonStr).getAsJsonObject();
//        //再转JsonArray 加上数据头
        JsonArray jsonArray = jsonObject.getAsJsonArray("array");
        return jsonArray != null;*/
    }

    /**
     * 解析http请求，获取http body中的内容，转化为字符串
     *
     * @param req: Http请求
     * @return java.lang.String
     * @author He Peng
     * @date 2021/8/24 10:56
     */
    public static String getHttpBody(HttpServletRequest req) {
        String httpBody = null;
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(req.getInputStream(), StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            String temp;
            while ((temp = br.readLine()) != null) {
                sb.append(temp);
            }
            br.close();
            httpBody = sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return httpBody;
    }

    /**
     * 利用正则表达式判断字符串是否是数字
     *
     * @param str: 字符串，不为空
     * @return boolean
     * @author He Peng
     * @date 2021/8/9 19:32
     */
    private static boolean isNumeric(String str) {
        Matcher isNum = NUMBER_PATTERN.matcher(str);
        return isNum.matches();
    }

    /**
     * 对于不同的注解（@RequestParma和@RequestBody）获取不同地方的参数
     *
     * @param method:
     * @param req:
     * @return java.lang.Object[]
     * @author Jiutwo
     * @date 2021/8/5 14:06
     */
    public Object[] getParamValuesFromHttpMsg(Method method, HttpServletRequest req) {
        //获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取请求url中的参数
        Map<String, String[]> parameterMap = req.getParameterMap();
        // 获取body中的数据
        String json = getHttpBody(req);
        // 获取方法参数上的注解名称，不同的注解采取不同的获取参数的策略，参数类型paramTypes和参数注解annotationNames应该是一一对应的
        List<String> annotationNames = getParameterAnnotation(method);
        //保存参数值
        Object[] paramValues = new Object[parameterTypes.length];
        //方法的参数列表
        for (int i = 0; i < parameterTypes.length; i++) {
            //根据参数名称，做某些处理
            String requestParam = parameterTypes[i].getSimpleName();
            // 判断是否是注解@MyRequestParam
            if (MyRequestParam.class.getSimpleName().equals(annotationNames.get(i))) {
                for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                    String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                    if (isNumeric(value)) {
                        paramValues[i] = Integer.parseInt(value);
                    } else {
                        paramValues[i] = value;
                    }
                }
            }
            // 判断是否是注解@MyResonseBody
            if (MyRequestBody.class.getSimpleName().equals(annotationNames.get(i)) && null != json) {
                try {
                    Object obj = fromJson(json, Class.forName(parameterTypes[i].getName()));
                    paramValues[i] = obj;

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return paramValues;
    }

    /**
     * 基于xml后的http消息转化
     *
     * @param method: url对应Method方法
     * @param req:    request，用于获取http中的参数
     * @return java.lang.Object[]
     * @author He Peng
     * @date 2021/8/14 16:29
     */
    public Object[] getParamFromHttpMsg(Method method, HttpServletRequest req, HttpServletResponse resp) {

        //获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取请求url中的参数
        Map<String, String[]> parameterMap = req.getParameterMap();
        // 标兵，已经访问的参数个数
        int count = 0;
        //保存参数值
        Object[] paramValues = new Object[parameterTypes.length];

        for (int i = 0; i < parameterTypes.length; i++) {
            if (!notInList(EXCLUDE, parameterTypes[i].getSimpleName()) && MultipartFile.class != parameterTypes[i]) {
                int x = 0;
                for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                    if (x < count) {
                        x++;
                        continue;
                    }
                    String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                    // 如果匹配到则退出循环
                    if (Integer.class == parameterTypes[i]) {
                        if (isNumeric(value)) {
                            paramValues[i] = Integer.parseInt(value);
                        } else {
                            throw new HandwritingRuntimeException(ResultCodeEnum.PARAM_ERROR);
                        }
                        break;
                    } else if (Long.class == parameterTypes[i]) {
                        paramValues[i] = Long.parseLong(value);
                        break;
                    } else if (String.class == parameterTypes[i]) {
                        paramValues[i] = value;
                        break;
                    } else if (Float.class == parameterTypes[i]) {
                        paramValues[i] = Float.parseFloat(value);
                        break;
                    } else if (Double.class == parameterTypes[i]) {
                        paramValues[i] = Double.parseDouble(value);
                        break;
                    }
                }
                count++;
            }
            // 如果是文件类型(一个)
            else if (MultipartFile.class == parameterTypes[i]) {
                if (MultipartFileResolver.isMultipart(req)) {
                    MultipartFileResolver fileResolver = new MultipartFileResolver();
                    List<MultipartFile> multipartFiles = fileResolver.parseRequestToMultipartFile(req);
                    paramValues[i] = multipartFiles.get(0);
                } else {
                    log.warning("http请求中不存在Multipart content，但是参数却是Multipart类型");
                }
            }
            // 处理List类型的参数
            else if (java.util.List.class == parameterTypes[i]) {
                // 如果是文件类型(多个)
                Class<?> actualType = getActualTypeInMethodParam(method);
                if (actualType == MultipartFile.class) {
                    MultipartFileResolver fileResolver = new MultipartFileResolver();
                    List<MultipartFile> multipartFiles = fileResolver.parseRequestToMultipartFile(req);
                    paramValues[i] = multipartFiles;
                } else {
                    // 获取url上相同类型的参数，如id
                    if (java.util.List.class == parameterTypes[i] && parameterTypes[i].getGenericSuperclass() == Integer.class) {
                        List<Integer> tmp = new ArrayList<>();
                        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                            String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                            if (isNumeric(value)) {
                                tmp.add(Integer.parseInt(value));
                            } else {
                                throw new HandwritingRuntimeException(ResultCodeEnum.PARAM_ERROR);
                            }
                        }
                        paramValues[i] = tmp;
                    } else if (java.util.List.class == parameterTypes[i] && parameterTypes[i].getGenericSuperclass() == Long.class) {
                        List<Long> tmp = new ArrayList<>();
                        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                            String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                            if (isNumeric(value)) {
                                tmp.add(Long.parseLong(value));
                            } else {
                                throw new HandwritingRuntimeException(ResultCodeEnum.PARAM_ERROR);
                            }
                        }
                        paramValues[i] = tmp;
                    } else if (java.util.List.class == parameterTypes[i] && parameterTypes[i].getGenericSuperclass() == String.class) {
                        List<String> tmp = new ArrayList<>();
                        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                            String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                            tmp.add(value);
                        }
                        paramValues[i] = tmp;
                    } else {
                        // 对于数值类型需要由double进行转型
                        Class clazz = getActualTypeInMethodParam(method);
                        String json = getHttpBody(req);
                        if (validateJson(json)) {
                            if (clazz == Long.class || clazz == Integer.class || clazz == Double.class) {
                                List tmpList = parseHaveHeaderJArray(json, clazz);
                                List<Object> resList = new ArrayList<>();
                                if (tmpList.size() > 0) {
                                    if (tmpList.get(0) instanceof Double) {
                                        if (clazz == Long.class) {
                                            for (Object obj : tmpList) {
                                                resList.add((((Double) obj).longValue()));
                                            }
                                        } else if (clazz == Integer.class) {
                                            for (Object obj : tmpList) {
                                                resList.add(((Double) obj).intValue());
                                            }
                                        }
                                    }
                                }
                                paramValues[i] = resList;
                            } else {
                                // 参数为非数字类型，直接存入
                                List<Object> tmp = parseHaveHeaderJArray(json, clazz);
                                paramValues[i] = tmp;
                            }
                        } else if (parameterTypes.length == 1 && actualType == java.lang.Long.class) {
                            List<Long> tmp = new ArrayList<>();
                            for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                                String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                                if (isNumeric(value)) {
                                    tmp.add(Long.parseLong(value));
                                } else {
                                    throw new HandwritingRuntimeException(ResultCodeEnum.PARAM_ERROR);
                                }
                            }
                            paramValues[i] = tmp;
                        } else {
                            throw new HandwritingRuntimeException(ResultCodeEnum.PARAM_ERROR);
                        }
                    }
                }
            }
            // 对于User等实体类类型(非List类型)，获取http中的json然后，转化为对象
            else if (notInList(EXCLUDE, parameterTypes[i].getSimpleName())) {
                if (parameterTypes[i] == HttpServletRequest.class) {
                    paramValues[i] = req;
                } else if (parameterTypes[i] == HttpServletResponse.class) {
                    paramValues[i] = resp;
                }
                else {
                    try {
                        // 获取 Http body 中的数据
                        String json = getHttpBody(req);
                        // 将json转为对象
                        Object obj = fromJson(json, Class.forName(parameterTypes[i].getName()));
                        paramValues[i] = obj;
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return paramValues;
    }

    /**
     * 获取Method方法的中的List集合的真正类型
     *
     * @param method: Method方法
     * @return java.lang.Class<?>
     * @author He Peng
     * @date 2021/8/24 17:05
     */
    private Class<?> getActualTypeInMethodParam(Method method) {
        for (Parameter parameter : method.getParameters()) {
            String typeName = parameter.getParameterizedType().getTypeName();
            int flag = typeName.indexOf(List.class.getName());
            if (flag != -1) {
                String fullName = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">"));
                try {
                    return Class.forName(fullName);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 解析没有数据头的纯数组
     *
     * @param json:  json字符串
     * @param clazz: class对象，表示转化的类型
     * @return java.util.List<T>
     * @author He Peng
     * @date 2021/8/24 15:18
     */
    private <T> List<T> parseNoHeaderJsonArray(String json, Class<T> clazz) {
        if (json == null || "".equals(json)) {
            return null;
        }
        //Json的解析类对象
        JsonElement jsonElement = JsonParser.parseString(json);
        JsonArray jsonArray = jsonElement.getAsJsonArray();
        Gson gson = new Gson();
        List<T> list = new ArrayList<>();

        //加强for循环遍历JsonArray
        for (JsonElement user : jsonArray) {
            //使用GSON，直接转成Bean对象
            T t = gson.fromJson(user, clazz);
            list.add(t);
        }
        return list;
    }

    /**
     * 解析有数据头的纯数组
     */
    private List<Object> parseHaveHeaderJArray(String json, Class<Object> clazz) {
        //先转JsonObject
        JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
        //再转JsonArray 加上数据头
        JsonArray jsonArray = jsonObject.getAsJsonArray("array");
        if (jsonArray == null) {
            parseNoHeaderJsonArray(json, clazz);
        }
        Gson gson = new Gson();
        List<Object> list = new ArrayList<>();
        //循环遍历
//        List<LinkedTreeMap> tmpList = new ArrayList<>();
        boolean flag = notInList(EXCLUDE, clazz.getSimpleName());
        List<Map> mapList = new ArrayList<>();
        for (JsonElement element : jsonArray) {
            //通过反射
            if (flag) {
                Object t = gson.fromJson(element, clazz);
                list.add(t);
            } else {
                Map map = gson.fromJson(element, Map.class);
                mapList.add(map);
//                T t = gson.fromJson(element, new TypeToken<T>() {
//                }.getType());
//                tmpList.add((LinkedTreeMap) t);
            }
        }
        // 拿出数据
//        if (!flag) {
//            for (LinkedTreeMap linkedTreeMap : tmpList) {
//                for (Map.Entry<String, T> next : (Iterable<Map.Entry<String, T>>) linkedTreeMap.entrySet()) {
//                    list.add(next.getValue());
//                }
//            }
//        }
        if (!flag) {
            for (Map map : mapList) {
                for (Object value : map.values()) {
                    if (value instanceof String) {
                        // 类型推断
                        if (clazz.getSimpleName().equals(Integer.class.getSimpleName())) {
                            list.add(Integer.parseInt((String) value));
                        } else if (clazz.getSimpleName().equals(Long.class.getSimpleName())) {
                            list.add(Long.parseLong((String) value));
                        } else if (clazz.getSimpleName().equals(Double.class.getSimpleName())) {
                            list.add(Double.parseDouble(((String) value)));
                        } else {
                            list.add(value);
                        }
                    } else {
                        list.add(value);
                    }

                }
            }
        }

        return list;
    }

    /**
     * 判断某个对象是否存在于列表
     *
     * @param list: 列表
     * @param t:    对象
     * @return boolean
     * @author He Peng
     * @date 2021/8/24 10:57
     */
    private <T> boolean notInList(List<T> list, T t) {
        for (T t1 : list) {
            if (t1.equals(t)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 获取方法参数上的注解，返回一个int数组
     *
     * @param method: Controller的方法
     * @return java.util.List<java.lang.String>
     * @author He Peng
     * @date 2021/8/7 19:00
     */
    private List<String> getParameterAnnotation(Method method) {
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

        List<String> annotationNames = new ArrayList<>();

        if (parameterAnnotations.length == 0) {
            return null;
        }
        int i = 0;
        for (Annotation[] parameterAnnotation : parameterAnnotations) {
            for (Annotation annotation : parameterAnnotation) {
                if (annotation instanceof MyRequestBody) {
                    annotationNames.add(MyRequestBody.class.getSimpleName());
                }
                if (annotation instanceof MyRequestParam) {
                    annotationNames.add(MyRequestParam.class.getSimpleName());
                }
            }
        }
        return annotationNames;
    }


}


